We are now ready to debug and test the application. We will not cover all aspects of debugging and testing in this example, but we will illustrate using the monitor card. The monitor card is shown in Figure 14.
We will test our application in the backward chaining mode. Go to the monitor card. If the "Forward Chain/Backward Chain" field shows "Forward chain", click on it to change it to "Backward chain". Next, set the check boxes for "trace" and "step" to true (an "x" will appear in the box when it is set true). Next, enter "model" in the goal field followed by a carriage return. The field script initializes the system and invokes the inference engine.
Since we have "step" checked, the system stops after each major operation. When it stops with the monitor card showing, click on "continue" to restart it. During this example, use the "continue" button on the monitor card to resume execution each time the system stops at the monitor card.
Since trace is enabled, the system displays its actions in the "Trace" field. The system also displays facts and their values in the "Fact" field and the status of the stack in the "Stack" field.
NOTE: The "Stack" field on the monitor card grows down. Items are pushed and
popped at the bottom of the "Stack" field.
The system begins by checking for a card named "model" (our goal). Since there is no card by that name, it attempts to execute a handler by that name. Since there is no handler to set the fact, it begins examining rules that can assert the fact.
The first rule capable of asserting fact "model" is rule "Corvette". In processing rule "Corvette", the system encounters fact "car" which has not been asserted. Again it first checks for a card by that name and then for a handler by that name. Since we have neither in our stack, it searches for a rule capable of asserting the fact.
The first rule capable of asserting fact "car" is rule "car 1" so the system pushes it on the stack for processing. In processing rule "car 1", the system encounters fact "wheels" and pushes it on the stack for processing. Since we have a card by that name, the system goes to that card and waits to be restarted.
Here we will examine the operation of the bar and arrow (|<-) button. Click on the button and observe that it takes you back to the monitor card. Now, click on the arrow and bar (->|) button and observe that it takes you back to the
"wheels" fact card. You can use these buttons to toggle between the monitor card and the currently displayed card. Remember, if you use this feature, you should always return to the fact card via the arrow and bar (->|) button. If you use the "continue" button on the monitor card, it will restart the inference engine without asserting the fact. In the case of fact "wheels", this would result in the fact being set to "?" and all attempts to match the fact would fail, thus preventing the system from finding a solution.
After returning to the "wheels" fact card, enter "4" followed by carriage return in the field. The script for that field will assert fact "wheels" to be "4" and the system will push fact "wheels" on the stack in order to process any daemons associated with it. Then, the script restarts the inference engine. Upon restarting, the system finds that there are no daemons using fact "wheels" so it pops that "wheels" off the stack. Since fact "wheels" has now been asserted, the system pops that instance of fact "wheels" from the stack.
Now the system resumes processing the rule "car 1" and encounters fact
"passengers" which is not yet asserted. Again, since there is a card with that name, the system goes to that card. Again we are presented with a field for our answer. Enter "4" for the number of passengers followed by a carriage return. The field script asserts the fact and restarts the chaining process.
After checking for daemons and popping both occurrences of fact "passengers" from the stack, the system resumes executing rule "car 1" and encounters fact
"used for cargo" which is not yet asserted. Since there is a card with that name, the system goes to that card. On the card, we are presented three options;
"yes", "no", and "don't know". To illustrate the use of the wildcard (*), click on "don't know". When the inference engine restarts and returns you to the monitor card, verify that the fact has been set to "*" and that the clause containing "used for cargo" proved true. The wildcard forces the match. If we had put "" (null string) into fact "used for cargo" the system would have continued searching for a way to assert the fact (null indicates "not asserted"). After finding none, the system would have set the fact to "?", which would have caused the clause to evaluate false. Similarly, had we set the fact to "?" at the fact card, the clause would have evaluated false.
After checking for daemons and popping both occurrences of fact "used for cargo" from the stack, the system resumes executing rule "car 1". Since all of the facts in the conditional part of the rule have been asserted and the rule proves true, the "then" part of the rule is executed. The "then" part of the rule sets fact "car" to true.
After checking for daemons using fact "car" and popping fact "car" from the stack, the system finds that rule "car 1" is complete and pops it from the stack. Since fact "car" is now asserted, the system pops it from the stack. It then resumes processing of rule "Corvette". The next fact required by rule "Corvette" is "make". The system pushes it on the stack and looks for a card with that name. It then goes to the card and waits to be restarted. Enter "Chevy" followed by carriage return in the field. The field script sets fact "make" to
"Chevy" and restarts the inference engine.
After checking for daemons using fact "make" and popping it from the stack, the system resumes processing rule "Corvette". It finds that the "make" clause evaluated true and evaluates the clause containing fact "passengers". Fact
"passengers" has already been asserted. Since it does not match the clause, the clause proves false and the rule proves false. Since there is no ELSE clause, the system pops rule "Corvette" from the stack.
The system resumes processing fact "model", which is the goal. It finds that the next rule capable of setting that fact is rule "Camaro", so it pushes rule
"Camaro" on the stack and begins processing it.
All of the facts necessary to evaluate rule "Camaro" have been asserted and the rule evaluates true. Since it evaluates true, the system processes the "then" part of the rule, setting fact "model" to "Camaro". After checking for daemons using fact "model" and popping fact "model" from the stack, the system resumes processing rule "Camaro", processes the "go to" statement, goes to card "Camaro", and waits to be restarted.
The card "Camaro" announces the solution. Click on the "continue" button to restart the inference engine. When it starts, it finds that rule "Camaro" is finished and pops it from the stack. It then finds that fact "model" has been asserted and pops it from the stack. Since the stack is empty, the system goes to the card from which the "init" command was sent (the monitor card in this case) and stops.
Now, let's add some new capability to our system. First we will give the user a chance to recognize a car rather than forcing him to answer the questions about wheels, passengers, and cargo to establish the fact. We do that by creating another fact card for car. Copy the sample fact card and modify it so that it looks like the card in Figure 15. Name the card "car". Next, delete the "answer" field and the card script (the script clears the field and positions the cursor in it). Then, delete the "don't know" buttons that store "?" and "*" into the fact, leaving the one that sets the fact to a null string (""). Modify the scripts for the "yes" and "no" buttons to store "true" and "false" into the fact instead of "yes" and "no" (the rules expect "true" or "false"). Finally, if you plan to use forward chaining, enter "car" at the front of the fact declarations in the rule base.
If you use forward chaining, you will need to recompile the rule base (since you added "car" to the fact declarations). If you backward chaining, you do not need to recompile (the fact declarations are only used to impose an order for queries when forward chaining).
Start the system by clicking on the "init" button on the "monitor" card. You will find that the system will prompt for fact "car" via the card. If you answer "yes" or "no", it will not prompt you for "wheels" or "used for cargo". Since "passengers" is used in rules "Corvette" and "Camaro", the system will still prompt for "passengers". If you answer "don't know" to the query about "car", the system will consider the fact "not asserted" and will continue trying to determine "car" and will use rule "car 1" to do so. We could achieve the same effect in the "don't know" button by completely omitting the
"putIntoFact" command from the script. However, the setting of the fact to null will cause the system to check for daemons using the fact, whereas omitting the
"putIntoFact" command precludes this.
Next, run the system answering the queries as follows:
for fact: answer:
"car" "yes"
"make" "Chevy"
"passengers" "*" (enter "*" followed by carriage return in the field)
You will find that although the facts should fit both "Corvette" and "Camaro", the system stops after reporting "Corvette" as a solution. This is because, when the system finds a solution to a fact while backward chaining, it searches no further for solutions to that fact. To force the system to search for all solutions, make the following changes to the rule base:
Change line:
put "Corvette" into fact "model"
to
put "" into fact "model"
Change line:
put "Camaro" into fact "model"
to
put "" into fact "model"
Change line:
put "Mustang" into fact "model"
to
put "" into fact "model"
Now, recompile the rule base and run the system, answering the queries as in the last example. You will find this time that the system will show both solutions
(Corvette and Camaro).
Next, we will demonstrate using a handler to set a fact. Edit the stack script to add the following handler:
on car
putIntoFact true, car
end car
Now go to the monitor card and rerun the example answering the queries as follows:
for fact: answer:
"car" "don't know"
"make" "Chevy"
"passengers" "*" (enter "*" followed by carriage return in the field)
You will find that the system will prompt for fact "car" and, when you choose
"don't know", it will execute the handler named "car". The handler will set fact "car" to true. If you choose "yes" or "no" to the "car" query, the system will not attempt to execute the handler as the fact will have been determined by your answer.
Next, we will demonstrate daemons. Daemons are evaluated when any fact used by them is asserted regardless of how the fact was asserted (by the "putIntoFact" XCMD, by a rule, or by another daemon). A daemon will evaluate facts as they are at the time that the daemon is examined. The inference engine will never search for solutions for facts in order to satisfy daemons nor will it examine rules as a result of a fact being asserted by a daemon.
Modify the rule base to add the following rules and daemons:
rule truck
if
fact wheels >= 4 and
fact "used for cargo" = yes
then
put "" into fact model
go to card "truck"
daemon "cargo 1"
when
not fact car
then
put yes into fact "used for cargo"
else
put no into fact "used for cargo"
Add a conclusion card for "truck". Remember to name the card "truck".
Recompile the rule base and run the system. Answer the queries as follows:
for fact: answer:
"car" "no"
"wheels" "4"
If you use the trace capability to monitor the actions of the inference engine, you will see that when you answer "no" to the "car" query, the system will execute the daemon. It will find the daemon true and will set "used for cargo" to "yes". Then, when it examines rule "truck", it will not ask about fact
"used for cargo" as that fact was asserted by the daemon.
You can experiment further with this rule base to gain a better understanding of how the system works. Since we included the fact declaration section, you can try forward chaining with the example.